using SharpDX;
using System.IO;
using System.Runtime.InteropServices;

namespace Engine.Graphics.VertexStructures
{
    public class MyOwnVertex
    {
        #region Public Structs

        [StructLayout(LayoutKind.Explicit)]
        public struct C11SyndicateVertexData : IGameVertex
        {
            [FieldOffset(0)]
            public Vector2 UV;

            [FieldOffset(8)]
            public Vector2 UV2;

            [FieldOffset(16)]
            private Color4 diffuseColor;

            [FieldOffset(32)]
            private Color4 vertexColor;

            public C11SyndicateVertexData(float u1, float v1, float u2, float v2, Color4 vertexColor)
                : this(u1, v1, u2, v2, vertexColor, Color4.Black)
            {
            }

            public C11SyndicateVertexData(float u1, float v1, float u2, float v2, Color4 vertexColor, Color4 diffuseColor)
            {
                UV.X = u1;
                UV.Y = v1;
                UV2.X = u2;
                UV2.Y = v2;
                this.vertexColor = vertexColor;
                this.diffuseColor = diffuseColor;
            }

            public Color4 DiffuseColor
            {
                get { return diffuseColor; }
                set { diffuseColor = value; }
            }

            public float Tu
            {
                get { return this.UV.X; }
                set { this.UV.X = value; }
            }

            public float Tv
            {
                get { return this.UV.Y; }
                set { this.UV.Y = value; }
            }

            public float Tu2
            {
                get { return this.UV2.X; }
                set { this.UV2.X = value; }
            }

            public float Tv2
            {
                get { return this.UV2.Y; }
                set { this.UV2.Y = value; }
            }

            public Color4 VertexColor
            {
                get { return this.vertexColor; }
                set { this.vertexColor = value; }
            }

            public static int SizeInBytes()
            {
                return Marshal.SizeOf(typeof(Wr2Data));
            }

            public static C11SyndicateVertexData FromStream(BinaryReader br, int Version)
            {
                switch (Version)
                {
                    case 0:
                        return LoadVersion0(br);

                    case 1:
                        return LoadVersion1(br);

                    default:
                        throw new System.Exception("World Racing 2 Vertex could not load Version: " + Version.ToString());
                }
            }

            private static C11SyndicateVertexData LoadVersion0(BinaryReader br)
            {
                C11SyndicateVertexData vertex = new C11SyndicateVertexData();
                // vertex Color
                float vertexColorAlpha = br.ReadSingle(); // 1.0 Shadow -> 0.0 no Shadow
                float vertexColorRed = br.ReadSingle();
                float vertexColorGreen = br.ReadSingle();
                float vertexColorBlue = br.ReadSingle();
                // Diffuse Color
                float diffuseAlpha = br.ReadSingle();
                float diffuseRed = br.ReadSingle();
                float diffuseGreen = br.ReadSingle();
                float diffuseBlue = br.ReadSingle();

                vertex.DiffuseColor = new Color4(diffuseRed, diffuseGreen, diffuseBlue, diffuseAlpha);
                vertex.VertexColor = new Color4(vertexColorRed, vertexColorGreen, vertexColorBlue, 0.0f);

                //vertex.DiffuseColor = new Color4(0.4f, 0.4f, 0.4f, 0.0f);
                //vertex.VertexColor = new Color4(1.0f, 0.4f, 0.4f, 1.0f);

                return vertex;
            }

            private static C11SyndicateVertexData LoadVersion1(BinaryReader br)
            {
                C11SyndicateVertexData vertex = new C11SyndicateVertexData();

                // UV Coordinates
                vertex.Tu = br.ReadSingle();
                vertex.Tv = br.ReadSingle();
                vertex.Tu2 = br.ReadSingle();
                vertex.Tv2 = br.ReadSingle();

                // vertex Color
                float vertexColorAlpha = br.ReadSingle(); // 1.0 Shadow -> 0.0 no Shadow
                float vertexColorRed = br.ReadSingle();
                float vertexColorGreen = br.ReadSingle();
                float vertexColorBlue = br.ReadSingle();
                // Diffuse Color
                float diffuseAlpha = br.ReadSingle();
                float diffuseRed = br.ReadSingle();
                float diffuseGreen = br.ReadSingle();
                float diffuseBlue = br.ReadSingle();

                vertex.DiffuseColor = new Color4(diffuseRed, diffuseGreen, diffuseBlue, diffuseAlpha);
                vertex.VertexColor = new Color4(vertexColorRed, vertexColorGreen, vertexColorBlue, 0.0f);

                //vertex.DiffuseColor = new Color4(0.4f, 0.4f, 0.4f, 0.0f);
                //vertex.VertexColor = new Color4(1.0f, 0.4f, 0.4f, 1.0f);

                return vertex;
            }

            public void ToStream(BinaryWriter bw)
            {
                bw.Write(this.Tu);
                bw.Write(this.Tv);

                bw.Write(this.Tu2);
                bw.Write(this.Tv2);

                bw.Write(this.vertexColor.Alpha);
                bw.Write(this.vertexColor.Red);
                bw.Write(this.vertexColor.Green);
                bw.Write(this.vertexColor.Blue);

                bw.Write(this.DiffuseColor.Alpha);
                bw.Write(this.DiffuseColor.Red);
                bw.Write(this.DiffuseColor.Green);
                bw.Write(this.DiffuseColor.Blue);
            }

            public int GetCurrentVersion()
            {
                // Version 1: 01.02.2017
                return 1;
            }

            public IGameVertex UpdateWithEngineVertexData(PositionNormalTextured positionNormalTextured)
            {
                this.UV = positionNormalTextured.UV;
                return this;
            }
        }

        [StructLayout(LayoutKind.Explicit)]
        public struct PositionColor
        {
            [FieldOffset(0)]
            public Vector3 position;

            [FieldOffset(12)]
            public Color4 color;

            public PositionColor(Vector3 position, Color4 color)
            {
                this.position = position;
                this.color = color;
            }
        }

        [StructLayout(LayoutKind.Explicit)]
        public struct PositionNormal
        {
            [FieldOffset(0)]
            public Vector3 Position;

            [FieldOffset(12)]
            public Vector3 Normal;

            public float X
            {
                get { return this.Position.X; }
                set { this.Position.X = value; }
            }

            public float Y
            {
                get { return this.Position.Y; }
                set { this.Position.Y = value; }
            }

            public float Z
            {
                get { return this.Position.Z; }
                set { this.Position.Z = value; }
            }

            public PositionNormal(Vector3 position, Vector3 vector3)
            {
                this.Position = position;
                this.Normal = vector3;
            }

            internal static int SizeInBytes()
            {
                return Marshal.SizeOf(typeof(PositionNormal));
            }
        }

        [StructLayout(LayoutKind.Explicit)]
        public struct PositionNormalTextured
        {
            [FieldOffset(0)]
            public Vector3 Position;

            [FieldOffset(12)]
            public Vector3 Normal;

            [FieldOffset(24)]
            public Vector2 UV;

            public override int GetHashCode()
            {
                return this.Position.GetHashCode() ^ this.Normal.GetHashCode() ^ this.UV.GetHashCode();
            }
            public PositionNormalTextured(Vector3 Position, Vector3 Normal, Vector2 UV)
            {
                this.Position = Position;
                this.Normal = Normal;
                this.UV = UV;
            }

            public float X
            {
                get { return this.Position.X; }
                set { this.Position.X = value; }
            }

            public float Y
            {
                get { return this.Position.Y; }
                set { this.Position.Y = value; }
            }

            public float Z
            {
                get { return this.Position.Z; }
                set { this.Position.Z = value; }
            }

            public static int SizeInBytes()
            {
                int size = Marshal.SizeOf(typeof(PositionNormalTextured));
                return size;
            }

            public static PositionNormalTextured FromStream(BinaryReader br)
            {
                PositionNormalTextured vertex = new PositionNormalTextured();

                vertex.Position.X = br.ReadSingle();
                vertex.Position.Y = br.ReadSingle();
                vertex.Position.Z = br.ReadSingle();

                vertex.Normal.X = br.ReadSingle();
                vertex.Normal.Y = br.ReadSingle();
                vertex.Normal.Z = br.ReadSingle();

                vertex.UV.X = br.ReadSingle();
                vertex.UV.Y = br.ReadSingle();

                return vertex;
            }

            internal void ToStream(BinaryWriter bw)
            {
                // Write Position;
                bw.Write(this.Position.X);
                bw.Write(this.Position.Y);
                bw.Write(this.Position.Z);
                // Write Normal;
                bw.Write(this.Normal.X);
                bw.Write(this.Normal.Y);
                bw.Write(this.Normal.Z);
                // Write UV;
                bw.Write(this.UV.X);
                bw.Write(this.UV.Y);
            }
        }

        #endregion Public Structs

        #region Private Structs

        [StructLayout(LayoutKind.Explicit)]
        private struct MyPositionNormalTextured
        {
            [FieldOffset(0)]
            private Vector3 Position;

            [FieldOffset(12)]
            private Vector3 Normal;

            [FieldOffset(24)]
            private Wr2Data wrData;

            public float Nx
            {
                get { return this.Normal.X; }
                set { this.Normal.X = value; }
            }

            public float Ny
            {
                get { return this.Normal.Y; }
                set { this.Normal.Y = value; }
            }

            public float Nz
            {
                get { return this.Normal.Z; }
                set { this.Normal.Z = value; }
            }

            public float X
            {
                get { return this.Position.X; }
                set { this.Position.X = value; }
            }

            public float Y
            {
                get { return this.Position.Y; }
                set { this.Position.Y = value; }
            }

            public float Z
            {
                get { return this.Position.Z; }
                set { this.Position.Z = value; }
            }

            public static bool operator !=(MyPositionNormalTextured a, MyPositionNormalTextured b)
            {
                //if (a.GetHashCode() != b.GetHashCode())
                //    return true;
                //return false;
                if (a.Position == b.Position &&
                    a.Normal == b.Normal &&
                    a.wrData.UV.Equals(b.wrData.UV) &&
                    a.wrData.VertexColor.Equals(b.wrData.VertexColor) &&
                    a.wrData.DiffuseColor.Equals(b.wrData.DiffuseColor))
                    return false;

                return true;
            }

            public static bool operator ==(MyPositionNormalTextured a, MyPositionNormalTextured b)
            {
                //if (a.GetHashCode() != b.GetHashCode())
                //    return false;
                //return true;
                if (a.Position != b.Position)
                    return false;
                if (a.Normal != b.Normal)
                    return false;
                if (a.wrData.UV != b.wrData.UV)
                    return false;
                if (a.wrData.VertexColor != b.wrData.VertexColor)
                    return false;
                //MyPositionNormalTextured.doubleVerticesFound++;
                return true;
            }

            public static MyPositionNormalTextured FromStream(BinaryReader br)
            {
                MyPositionNormalTextured vertex = new MyPositionNormalTextured();

                vertex.Position.X = br.ReadSingle();
                vertex.Position.Y = br.ReadSingle();
                vertex.Position.Z = br.ReadSingle();

                vertex.Normal.X = br.ReadSingle();
                vertex.Normal.Y = br.ReadSingle();
                vertex.Normal.Z = br.ReadSingle();

                vertex.wrData.UV.X = br.ReadSingle();
                vertex.wrData.UV.Y = br.ReadSingle();

                br.BaseStream.Position += 8;

                // Diffuse Color
                float diffuseAlpha = br.ReadSingle();
                float diffuseRed = br.ReadSingle();
                float diffuseGreen = br.ReadSingle();
                float diffuseBlue = br.ReadSingle();

                // vertex Color
                float vertexColorAlpha = br.ReadSingle();
                float vertexColorRed = br.ReadSingle();
                float vertexColorGreen = br.ReadSingle();
                float vertexColorBlue = br.ReadSingle();

                vertex.wrData.DiffuseColor = new Color4(diffuseRed, diffuseGreen, diffuseBlue, diffuseAlpha);
                vertex.wrData.VertexColor = new Color4(vertexColorRed, vertexColorGreen, vertexColorBlue, vertexColorAlpha);

                return vertex;
            }

            private void UpdateBinormalTangent()
            {
            }

            public MyPositionNormalTextured(Vector3 position, Vector3 normal, float Tu, float Tv, float Tu2, float Tv2, Color4 vertexColor)
            {
                this.Position = position;
                this.Normal = normal;

                this.wrData = new Wr2Data();
                this.wrData.UV = new Vector2(Tu, Tv);//, 0.0f, 0.0f);
                this.wrData.UV2 = new Vector2(Tu2, Tv2);

                // defuse Color: XXX, R, G, B
                this.wrData.DiffuseColor = new Color4()
                {
                    Red = 0.4f,
                    Green = 0.4f,
                    Blue = 0.4f,
                    Alpha = 1.0f
                };
                // Vertex color for blending. alpha is for shadow
                this.wrData.VertexColor = vertexColor;
            }

            public MyPositionNormalTextured(Vector3 position, Vector3 normal, float Tu, float Tv, float Tu2, float Tv2, Color4 diffuseColor, Color4 blendColor)
                : this(position, normal, Tu, Tv, Tu2, Tv2, blendColor)
            {
                this.wrData.DiffuseColor = diffuseColor;
            }

            public void SaveToStream(BinaryWriter bw)
            {
                // Rite Position;
                bw.Write(this.Position.X);
                bw.Write(this.Position.Y);
                bw.Write(this.Position.Z);
                // Write Normal;
                bw.Write(this.Normal.X);
                bw.Write(this.Normal.Y);
                bw.Write(this.Normal.Z);
                // Write UV;
                bw.Write(this.wrData.UV.X);
                bw.Write(this.wrData.UV.Y);

                bw.Write(this.wrData.UV.X);
                bw.Write(this.wrData.UV.Y);

                // Write diffuseColr;
                bw.Write(this.wrData.DiffuseColor.Alpha);
                bw.Write(this.wrData.DiffuseColor.Red);
                bw.Write(this.wrData.DiffuseColor.Green);
                bw.Write(this.wrData.DiffuseColor.Blue);
                // Write vertexColor;
                bw.Write(this.wrData.VertexColor.Alpha);
                bw.Write(this.wrData.VertexColor.Red);
                bw.Write(this.wrData.VertexColor.Green);
                bw.Write(this.wrData.VertexColor.Blue);
            }

            public void SetNormal(Vector3 normal)
            {
                this.Normal = normal;
            }

            public static int SizeInBytes()
            {
                return Marshal.SizeOf(typeof(MyPositionNormalTextured));
            }

            public override string ToString()
            {
                return ("Pos: " + this.Position.ToString() + " normal: " + this.Normal.ToString() + " UV: " + this.wrData.UV.ToString());
            }
        }

        [StructLayout(LayoutKind.Explicit)]
        private struct Wr2Data : IGameVertex
        {
            [FieldOffset(0)]
            public Vector2 UV;

            [FieldOffset(8)]
            public Vector2 UV2;

            [FieldOffset(16)]
            private Color4 diffuseColor;

            [FieldOffset(32)]
            private Color4 vertexColor;

            public Wr2Data(float u1, float v1, float u2, float v2, Color4 vertexColor)
                : this(u1, v1, u2, v2, vertexColor, Color4.Black)
            {
            }

            public Wr2Data(float u1, float v1, float u2, float v2, Color4 vertexColor, Color4 diffuseColor)
            {
                UV.X = u1;
                UV.Y = v1;
                UV2.X = u2;
                UV2.Y = v2;
                this.vertexColor = vertexColor;
                this.diffuseColor = diffuseColor;
            }

            public Color4 DiffuseColor
            {
                get { return diffuseColor; }
                set { diffuseColor = value; }
            }

            public float Tu
            {
                get { return this.UV.X; }
                set { this.UV.X = value; }
            }

            public float Tv
            {
                get { return this.UV.Y; }
                set { this.UV.Y = value; }
            }

            public float Tu2
            {
                get { return this.UV2.X; }
                set { this.UV2.X = value; }
            }

            public float Tv2
            {
                get { return this.UV2.Y; }
                set { this.UV2.Y = value; }
            }

            public Color4 VertexColor
            {
                get { return this.vertexColor; }
                set { this.vertexColor = value; }
            }

            public static int SizeInBytes()
            {
                return Marshal.SizeOf(typeof(Wr2Data));
            }

            public static Wr2Data FromStream(BinaryReader br, int Version)
            {
                switch (Version)
                {
                    case 0:
                        return LoadVersion0(br);

                    case 1:
                        return LoadVersion1(br);

                    default:
                        throw new System.Exception("World Racing 2 Vertex could not load Version: " + Version.ToString());
                }
            }

            private static Wr2Data LoadVersion0(BinaryReader br)
            {
                Wr2Data vertex = new Wr2Data();
                // vertex Color
                float vertexColorAlpha = br.ReadSingle(); // 1.0 Shadow -> 0.0 no Shadow
                float vertexColorRed = br.ReadSingle();
                float vertexColorGreen = br.ReadSingle();
                float vertexColorBlue = br.ReadSingle();
                // Diffuse Color
                float diffuseAlpha = br.ReadSingle();
                float diffuseRed = br.ReadSingle();
                float diffuseGreen = br.ReadSingle();
                float diffuseBlue = br.ReadSingle();

                vertex.DiffuseColor = new Color4(diffuseRed, diffuseGreen, diffuseBlue, diffuseAlpha);
                vertex.VertexColor = new Color4(vertexColorRed, vertexColorGreen, vertexColorBlue, 0.0f);

                //vertex.DiffuseColor = new Color4(0.4f, 0.4f, 0.4f, 0.0f);
                //vertex.VertexColor = new Color4(1.0f, 0.4f, 0.4f, 1.0f);

                return vertex;
            }

            private static Wr2Data LoadVersion1(BinaryReader br)
            {
                Wr2Data vertex = new Wr2Data();

                // UV Coordinates
                vertex.Tu = br.ReadSingle();
                vertex.Tv = br.ReadSingle();
                vertex.Tu2 = br.ReadSingle();
                vertex.Tv2 = br.ReadSingle();

                // vertex Color
                float vertexColorAlpha = br.ReadSingle(); // 1.0 Shadow -> 0.0 no Shadow
                float vertexColorRed = br.ReadSingle();
                float vertexColorGreen = br.ReadSingle();
                float vertexColorBlue = br.ReadSingle();
                // Diffuse Color
                float diffuseAlpha = br.ReadSingle();
                float diffuseRed = br.ReadSingle();
                float diffuseGreen = br.ReadSingle();
                float diffuseBlue = br.ReadSingle();

                vertex.DiffuseColor = new Color4(diffuseRed, diffuseGreen, diffuseBlue, diffuseAlpha);
                vertex.VertexColor = new Color4(vertexColorRed, vertexColorGreen, vertexColorBlue, 0.0f);

                //vertex.DiffuseColor = new Color4(0.4f, 0.4f, 0.4f, 0.0f);
                //vertex.VertexColor = new Color4(1.0f, 0.4f, 0.4f, 1.0f);

                return vertex;
            }

            public void ToStream(BinaryWriter bw)
            {
                bw.Write(this.Tu);
                bw.Write(this.Tv);

                bw.Write(this.Tu2);
                bw.Write(this.Tv2);

                bw.Write(this.vertexColor.Alpha);
                bw.Write(this.vertexColor.Red);
                bw.Write(this.vertexColor.Green);
                bw.Write(this.vertexColor.Blue);

                bw.Write(this.DiffuseColor.Alpha);
                bw.Write(this.DiffuseColor.Red);
                bw.Write(this.DiffuseColor.Green);
                bw.Write(this.DiffuseColor.Blue);
            }

            public int GetCurrentVersion()
            {
                // Version 1: 01.02.2017
                return 1;
            }

            public IGameVertex UpdateWithEngineVertexData(PositionNormalTextured positionNormalTextured)
            {
                this.UV = positionNormalTextured.UV;
                return this;
            }
        }

        #endregion Private Structs
    }
}